﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using HQPad.Models;
using HQPad.DAL;

namespace HQPad.Services
{
    /// <summary>Spell colours types.</summary>
    public enum SpellColour
    {
        Black,
        Blue,
        Brown,
        Green,
        Grey,
        Red,
        White,
        Yellow,
        All
    }

    /// <summary>Spell store types.</summary>
    public enum StoreType
    {
        None,
        Store,
        Sorceror,
        Enchanted
    }

    public interface ISpellService
    {
        Spell AddSpell(int ruleSetId, string slug, string name, string description, string range, string duration, bool extendable, int level, SpellColour colour, bool specialist, StoreType type, bool primary, bool isCommon, string groupName, string family);
        Spell GetSpell(int spellId);
        Spell GetSpell(int ruleSetId, string slug);
        Spell GetSpellByLegacy(int ruleSetId, string legacyName);
        IEnumerable<Spell> GetSpells(int ruleSetId);
        IEnumerable<Spell> GetSpellsByRequiredGroup(int ruleSetId, string groupName);
        void UpdateSpell(Spell spell);
        void DeleteSpell(int spellId);
        string CreateSpellSlug(string spellName);
        void Add(Spell spell);
        IEnumerable<Spell> GetAllowedSpells(int ruleSetId, string knownSpells, string colours, string colourLevels, string primaryColours, string specialistColours);
    }

    public class SpellService : ISpellService
    {
        private HQContext context = null;

        public SpellService(IUnitOfWork context)
        {
            this.context = (HQContext)context;
        }

        #region AddSpell
        /// <summary>Add a new spell record.</summary>
        /// <param name="ruleSetId">The parent rule set.</param>
        /// <param name="slug">A unique url slug for the spell.</param>
        /// <param name="name">The spell name.</param>
        /// <param name="description">The spell description.</param>
        /// <param name="range">The spell range.</param>
        /// <param name="duration">The spell duration.</param>
        /// <param name="extendable">Whether the spell is extendable.</param>
        /// <param name="level">The spell casting level.</param>
        /// <param name="colour">The spell colour.</param>
        /// <param name="specialist">Whether the spell is a specialist spell.</param>
        /// <param name="type">The type of spell store.</param>
        /// <param name="primary">Whether the spell is a primary only spell.</param>
        /// <returns></returns>
        public Spell AddSpell(int ruleSetId, string slug, string name, string description, string range, string duration,
            bool extendable, int level, SpellColour colour, bool specialist, StoreType type, bool primary, bool isCommon, string groupName, string family)
        {
            Spell spell = new Spell()
                {
                    RuleSetID = ruleSetId,
                    Colour = Enum.GetName(typeof(SpellColour), colour),
                    StoreType = Enum.GetName(typeof(StoreType), type),
                    Deleted = false,
                    LastUpdatedByUserID = 1,
                    LastUpdated = DateTime.Now,
                    Name = name,
                    Description = description,
                    Range = range,
                    Duration = duration,
                    IsExtendable = extendable,
                    Level = level,
                    IsPrimary = primary,
                    IsSpecialist = specialist,
                    Slug = slug,
                    IsCommon = isCommon,
                    GroupName = groupName,
                    GroupSlug = CreateSpellSlug(groupName),
                    Family = family
                };

            context.Spells.Add(spell);

            return spell;
        }
        #endregion

        #region GetSpell
        /// <summary>Get a spell by it's unique ID.</summary>
        /// <param name="spellId">A unique spell ID.</param>
        /// <returns>The spell with the unique spell ID or null.</returns>
        public Spell GetSpell(int spellId)
        {
            return context.Spells.Find(spellId);
        }

        /// <summary>Get a spell by it's unique url slug within a rule set.</summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="slug">A spell url slug, unique within a rule set.</param>
        /// <returns>The spell found or null.</returns>
        public Spell GetSpell(int ruleSetId, string slug)
        {
            return context.Spells
                .Where(s => s.RuleSetID == ruleSetId && s.Slug.ToLower() == slug.ToLower())
                .FirstOrDefault();
        }
        #endregion

        #region GetSpells
        /// <summary>Get all spells in a rule set.</summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <returns>All spells in a rule set id sorted by colour, spell name and level.</returns>
        public IEnumerable<Spell> GetSpells(int ruleSetId)
        {
            return from s in context.Spells
                   where s.RuleSetID == ruleSetId
                   orderby s.Colour , s.Family ascending , s.Level ascending
                   select s;
        }
        #endregion

        #region GetSpellsByRequiredGroup
        /// <summary>
        /// Get all spells that have a requirement on a group.
        /// </summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="groupName">A spell group slug</param>
        /// <returns></returns>
        public IEnumerable<Spell> GetSpellsByRequiredGroup(int ruleSetId, string groupName)
        {
            return from s in context.Spells
                   where s.RuleSetID == ruleSetId && s.RequiredGroupName == groupName
                   orderby s.Colour, s.Family ascending, s.Level ascending
                   select s;
        }
        #endregion

        #region UpdateSpell
        /// <summary>Mark a spell as updated so that is it saved to db.</summary>
        /// <param name="spell">The spell to update.</param>
        public void UpdateSpell(Spell spell)
        {
            context.Entry(spell).State = System.Data.EntityState.Modified;
        }
        #endregion

        #region DeleteSpell
        /// <summary>Delete a spell.</summary>
        /// <param name="spellId">The unique id of the spell to delete.</param>
        public void DeleteSpell(int spellId)
        {
            Spell spell = GetSpell(spellId);
            context.Entry(spell).State = System.Data.EntityState.Deleted;
        }
        #endregion

        #region CreateSpellSlug
        /// <summary>Create a spell slug from a spel name.</summary>
        /// <param name="spellName">The name of the spell</param>
        /// <returns>The standard format skill slug.</returns>
        public string CreateSpellSlug(string spellName)
        {
            return spellName
                .ToLower()
                .Replace("_", "")
                .Replace("-", "to")
                .Replace("&", "")
                .Replace("+", "")
                .Replace(",", "")
                .Replace(".", "")
                .Replace("'", "")
                .Replace(" ", "-")
                .Replace("--", "-")
                .Replace("--", "-");
        }
        #endregion

        #region Add
        /// <summary>Add a spell object to the database.</summary>
        /// <param name="spell">The spell objetc to add.</param>
        public void Add(Spell spell)
        {
            context.Spells.Add(spell);
        }
        #endregion

        #region GetAllowedSpells
        /// <summary>
        /// Get the list of allowed spell choices for a character.
        /// </summary>
        /// <param name="ruleSetId">The rule set id</param>
        /// <param name="knownSpells">A comma separated list of spells already chosen.  These are spell group names.</param>
        /// <param name="colours">A comma separated list of colours available (wizard and above).</param>
        /// <param name="colourLevels">A comma separated list of max levels for each colour.  Ordered the same as the colours available above.</param>
        /// <param name="primaryColourOfMagic">A comma separated list of primary access colours of magic.</param>
        /// <param name="specialistColourOfMagic">A comma separated list of specialist access colours of magic.</param>
        /// <returns></returns>
        public IEnumerable<Spell> GetAllowedSpells(int ruleSetId, string knownSpells, string colours, string colourLevels, string primaryColours, string specialistColours)
        {
            IEnumerable<Spell> allowedSpells = null;

            allowedSpells = context.Set<Spell>().SqlQuery(
                "[hqpad-character-allowed-spells] @rulesId, @knownList, @colours, @maxLevels, @primaryColours, @specialistColours",
                new SqlParameter("@rulesId", ruleSetId),
                new SqlParameter("@knownList", knownSpells),
                new SqlParameter("@colours", colours),
                new SqlParameter("@maxLevels", colourLevels),
                new SqlParameter("@primaryColours", primaryColours),
                new SqlParameter("@specialistColours", specialistColours));

            return allowedSpells;
        }
        #endregion

        #region GetSpellByLegacy
        /// <summary>
        /// Get a spell by a legacy (group) name.
        /// </summary>
        /// <param name="ruleSetId">A rule set id.</param>
        /// <param name="legacyName">The spell legacy name.</param>
        /// <returns>The spell found or null.</returns>
        public Spell GetSpellByLegacy(int ruleSetId, string legacyName)
        {
            return context.Spells
                .Where(s => s.RuleSetID == ruleSetId && s.GroupName.ToLower() == legacyName.ToLower())
                .FirstOrDefault();
        }
        #endregion
    }
}